/* The Generic List.  Works like a list.  Allows adding and removing, and indexing
 * Tyler Wozniak & Andrew Hall
 */

#ifndef LIST
#define LIST
#include "imports.h"
#include "Node.h"

template <typename T>
class List{
private:
	Node<T>* head; //Head of the list
	Node<T>* tail; //Tail of the list
	int size; //Size of the list
public:
	//Default constructor.  Creates an empty list.
	List()
	{
		size = 0;
		head = NULL;
		tail = NULL;
	}

	//Indexer for the list
	T operator[](int index) const
	{
		Node<T>* temp = head;

		assert(index >= 0 && index < size);
		for(int i = 0; i < index; i++)
		{
			temp = temp->nextNode;
		}

		return temp->data;
	}

	//Returns the length of the list
	int Length() const
	{
		return size;
	}

	//Resets the indexes in the list
	void reIndex()
	{
		Node<T>* temp = head;
		for(int i = 0; i < size; i++)
		{
			temp->index = i;
			temp = temp->nextNode;
		}
	}

	//The add function, using += operator.  Adds an element to the list with T data
	void operator+=(T data)
	{
		//Node to be added to list
		Node<T>* toBeAdded = new Node<T>(data,size);

		if(!head) //If the head is null, put this at the head of the list
		{
			head = toBeAdded;
			tail = head;
		}
		else //Otherwise, put it on the end of the list and move the tail pointer
		{
			tail->nextNode = toBeAdded;
			tail = tail->nextNode;
		}
		//Increase the size
		size++;
	}

	//Remove function using -= operator.  Removes the element with T data from the list.
	void operator-=(T data)
	{
		Node<T> temp(data); //In order 
		Node<T>* current = head;
		Node<T>* prev = &temp;
		bool done = false;

		while(current && !done)
		{
			if(current->data == data)
			{
				//If current is head, there is no previous node
				if(current == head)
				{
					prev = head;
					head = head->nextNode;
					size--;
					delete prev;
				}
				else if(current == tail)
				{
					tail = prev;
					delete current;
					size--;
				}
				else
				{
					prev->nextNode = current->nextNode;
					delete current;
					size--;
				}
				reIndex();
				done = true;
			}
			else
			{
				prev = current;
				current = current->nextNode;
			}
		}
	}

	//Removes the node at the specified index
	void removeAt(int index)
	{
		T dataToRemove = (*this)[index];
		*this -= dataToRemove;
	}

	//Returns whether the list is empty
	inline bool isEmpty() const
	{
		return head == NULL;
	}

	//Clears all the elements in the list
	void clear()
	{
		Node<T>* current = head;
		Node<T>* prev;
		while(current)
		{
			prev = current;
			current = current->nextNode;
			delete prev;
		}
		size = 0;
	}

	//Deep Copy Constructor
	List(const List<T>& other)
	{
		size = 0;
		head = NULL;
		tail = NULL;
		for(int i = 0; i < other.Length(); i++)
		{
			*this += other[i];
		}
	}

	//Copy assignment operator
	List<T> operator=(const List<T>& other)
	{
		clear();
		for(int i = 0; i < other.Length(); i++)
		{
			*this += other[i];
		}
		return *this;
	}

	//Destructor
	~List()
	{
		clear();
	}
};
#endif